#include "qemu/osdep.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "qemu/timer.h"
#include "sysemu/runstate.h"
#include "qemu/bitops.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "exec/memory.h"
#include "exec/address-spaces.h"
#include "qapi/visitor.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "trace.h"
#include "sysemu/watchdog.h"

#define TYPE_HS1803_WDG "hs1803"
#define HS1803_REGION_SIZE 0x1000
#define HS1803_WDG(obj) \
    OBJECT_CHECK(hs1803_state, (obj), TYPE_HS1803_WDG)


/* WDG_LOAD为WatchDog计数加载值寄存器。 */
#define HS1803_WDG_LOAD 0x000
/* WDG_VALUE为WatchDog计数寄存器。 */
#define HS1803_WDG_VALUE 0x004
/* WDG_CONTROL为WatchDog控制寄存器。 */
#define HS1803_WDG_CONTROL 0x008
/* WDG_INTCLR为WatchDog中断清除寄存器。 */
#define HS1803_WDG_INTCLR 0x0c
/* WDG_RIS为WatchDog原始中断状态寄存器。 */
#define HS1803_WDG_RIS 0x10
/* WDG_MIS为WatchDog屏蔽后中断状态寄存器。 */
#define HS1803_WDG_MIS 0x14
/* WDG_LOCK为WatchDog访问控制寄存器。 */
#define HS1803_WDG_LOCK 0xc00

typedef struct {
    uint32_t wdg_load;
    uint32_t wdg_value;
    uint32_t wdg_control;
    uint32_t wdg_intclr;
    uint32_t wdg_ris;
    uint32_t wdg_mis;
    uint32_t wdg_lock;
} hs1803_regs;

typedef struct {
    SysBusDevice parent_obj;
    MemoryRegion iomem;
    qemu_irq     irq;
    QEMUTimer *timer;
    hs1803_regs regs;
} hs1803_state;

static const VMStateDescription vmstate_hs1803 = {
    .name = TYPE_HS1803_WDG,
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_END_OF_LIST()
    }
};

static void hs1803_reset(DeviceState *d)
{
}

static uint64_t hs1803_read(void *opaque, hwaddr offset, unsigned size)
{
    hs1803_state *s = (hs1803_state *)opaque;
    hs1803_regs *regs = &s->regs;
    uint32_t ret = 0;
    switch (offset) {
        case HS1803_WDG_LOAD:
            return regs->wdg_load;
        case HS1803_WDG_VALUE:
            if (regs->wdg_value == 0) {
                regs->wdg_mis = regs->wdg_ris & regs->wdg_control;
            }
            return regs->wdg_value;
        case HS1803_WDG_CONTROL:
            return regs->wdg_control;
        case HS1803_WDG_INTCLR:
            break;
        case HS1803_WDG_RIS:
            return regs->wdg_ris;
        case HS1803_WDG_MIS:
            return regs->wdg_mis;
        case HS1803_WDG_LOCK:
            return regs->wdg_lock != 0x1ACCE551;
        default:
            return -1;
    }
    return ret;
}

static void hs1803_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    hs1803_state *s = (hs1803_state *)opaque;
    hs1803_regs *regs = &s->regs;
    switch (offset) {
        case HS1803_WDG_LOAD:
            if (regs->wdg_lock == 0x1ACCE551) {
                regs->wdg_load = val;
                if (val == 0) {
                    regs->wdg_mis = regs->wdg_control & 0x01;
                    break;
                }
                regs->wdg_value = val;
            }
            break;
        case HS1803_WDG_CONTROL:
            if (regs->wdg_lock == 0x1ACCE551) {
                if ((val&0x01)&&((regs->wdg_control&0x01)== 0)) {
                    regs->wdg_value = regs->wdg_load;
                }
                regs->wdg_control = val;
            }
            break;
        case HS1803_WDG_INTCLR:
        if (regs->wdg_lock == 0x1ACCE551) {
                regs->wdg_intclr = val;
                regs->wdg_mis = 0;
                regs->wdg_value = regs->wdg_load;
            }
            break;
        case HS1803_WDG_RIS:
            if (regs->wdg_lock == 0x1ACCE551) {
                regs->wdg_control = val;
                if (regs->wdg_value == 0) {
                    regs->wdg_mis = regs->wdg_ris & (regs->wdg_control & 0x01);
                }
            }
            break;
        case HS1803_WDG_MIS:
            if (regs->wdg_lock == 0x1ACCE551) {
                if (val == 1)
                    hs1803_write(opaque, HS1803_WDG_INTCLR, 0x00, size);
                regs->wdg_mis = val;
            }
            break;
        case HS1803_WDG_LOCK:
            regs->wdg_lock = val;
        default:
            break;
    }
}

static const MemoryRegionOps hs1803_ops = {
    .read = hs1803_read,
    .write = hs1803_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid.min_access_size = 4,
    .valid.max_access_size = 8,
    .impl.max_access_size = 4,
};

static void hs1803_init(Object *obj)
{
    DeviceState *dev = DEVICE(obj);
    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
    hs1803_state *s = HS1803_WDG(obj);

    hs1803_regs *regs = &(s->regs);
    memset(regs, 0, sizeof(hs1803_regs));
    regs->wdg_load = 0x07735940;
    regs->wdg_value = 0x07735940;
    regs->wdg_control = 0x00000003;
    memory_region_init_io(&s->iomem, obj, &hs1803_ops, s, "hs1803.wdg", HS1803_REGION_SIZE);
    sysbus_init_mmio(sd, &s->iomem);
    sysbus_init_irq(sd, &s->irq);
}

static void hs1803_realize(DeviceState *d, Error **errp)
{
}

static void hs1803_finalize(Object *obj)
{
}

static Property hs1803_properties[] = {
    DEFINE_PROP_END_OF_LIST(),
};

static void hs1803_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    dc->realize = hs1803_realize;
    dc->reset = hs1803_reset;
    dc->vmsd = &vmstate_hs1803;
    device_class_set_props(dc, hs1803_properties);
}

static const TypeInfo hs1803_info = {
    .name          = TYPE_HS1803_WDG,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(hs1803_state),
    .instance_init = hs1803_init,
    .instance_finalize = hs1803_finalize,
    .class_init    = hs1803_class_init,
};

static void hs1803_register_types(void)
{
    printf("hs1803_register_types\n");
    type_register_static(&hs1803_info);
}

type_init(hs1803_register_types)
